 /*******************************************************************************
  * Copyright (c) 2004, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.intro.impl.swt;


 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTError;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.ui.forms.IFormColors;
 import org.eclipse.ui.forms.events.HyperlinkAdapter;
 import org.eclipse.ui.forms.events.HyperlinkEvent;
 import org.eclipse.ui.forms.widgets.FormText;
 import org.eclipse.ui.forms.widgets.FormToolkit;
 import org.eclipse.ui.forms.widgets.Hyperlink;
 import org.eclipse.ui.forms.widgets.ImageHyperlink;
 import org.eclipse.ui.forms.widgets.Section;
 import org.eclipse.ui.forms.widgets.TableWrapData;
 import org.eclipse.ui.forms.widgets.TableWrapLayout;
 import org.eclipse.ui.internal.intro.impl.Messages;
 import org.eclipse.ui.internal.intro.impl.model.AbstractBaseIntroElement;
 import org.eclipse.ui.internal.intro.impl.model.AbstractIntroElement;
 import org.eclipse.ui.internal.intro.impl.model.IntroContentProvider;
 import org.eclipse.ui.internal.intro.impl.model.IntroGroup;
 import org.eclipse.ui.internal.intro.impl.model.IntroHTML;
 import org.eclipse.ui.internal.intro.impl.model.IntroImage;
 import org.eclipse.ui.internal.intro.impl.model.IntroLink;
 import org.eclipse.ui.internal.intro.impl.model.IntroSeparator;
 import org.eclipse.ui.internal.intro.impl.model.IntroText;
 import org.eclipse.ui.internal.intro.impl.model.loader.ContentProviderManager;
 import org.eclipse.ui.internal.intro.impl.model.url.IntroURLParser;
 import org.eclipse.ui.internal.intro.impl.util.DialogUtil;
 import org.eclipse.ui.internal.intro.impl.util.ImageUtil;
 import org.eclipse.ui.internal.intro.impl.util.Log;
 import org.eclipse.ui.internal.intro.impl.util.StringUtil;
 import org.eclipse.ui.internal.intro.impl.util.Util;
 import org.eclipse.ui.intro.config.IIntroContentProvider;
 import org.eclipse.ui.intro.config.IIntroContentProviderSite;

 /**
  * Factory to create all UI forms widgets for the Forms intro presentation.
  */
 public class PageWidgetFactory {

     protected HyperlinkAdapter hyperlinkAdapter = new HyperlinkAdapter() {

         public void linkActivated(HyperlinkEvent e) {
             String url = (String ) e.getHref();
             IntroURLParser parser = new IntroURLParser(url);
             if (parser.hasIntroUrl()) {
                 // execute the action embedded in the IntroURL
 parser.getIntroURL().execute();
                 return;
             } else if (parser.hasProtocol()) {
                 Util.openBrowser(url);
                 return;
             }
             DialogUtil.displayInfoMessage(((Control) e.getSource()).getShell(),
                 Messages.HyperlinkAdapter_urlIs + " " + url); //$NON-NLS-1$
 }

         public void linkEntered(HyperlinkEvent e) {
         }

         public void linkExited(HyperlinkEvent e) {
         }
     };


     protected FormToolkit toolkit;
     protected PageStyleManager styleManager;
     protected IIntroContentProviderSite site;


     /*
      * protect bad creation.
      */
     protected PageWidgetFactory(FormToolkit toolkit,
             PageStyleManager styleManager) {
         this.toolkit = toolkit;
         this.styleManager = styleManager;
     }

     public void setContentProviderSite(IIntroContentProviderSite site) {
         this.site = site;
     }

     public void createIntroElement(Composite parent,
             AbstractIntroElement element) {
         // check if this element is filtered, and if yes, do not create it.
 boolean isFiltered = getFilterState(element);
         if (isFiltered)
             return;

         Control c = null;
         switch (element.getType()) {
         case AbstractIntroElement.GROUP:
             IntroGroup group = (IntroGroup) element;
             c = createGroup(parent, group);
             updateLayoutData(c, element);
             // c must be a composite.
 Composite newParent = (Composite) c;
             if (c instanceof Section)
                 // client is a composite also.
 newParent = (Composite) ((Section) newParent).getClient();
             AbstractIntroElement[] children = group.getChildren();
             for (int i = 0; i < children.length; i++)
                 createIntroElement(newParent, children[i]);
             break;
         case AbstractIntroElement.LINK:
             IntroLink link = (IntroLink) element;
             c = createImageHyperlink(parent, link);
             updateLayoutData(c, element);
             break;
         case AbstractIntroElement.TEXT:
             IntroText text = (IntroText) element;
             c = createText(parent, text);
             updateLayoutData(c, element);
             break;
         case AbstractIntroElement.IMAGE:
             IntroImage image = (IntroImage) element;
             c = createImage(parent, image);
             if (c!=null)
                 updateLayoutData(c, element);
             break;
         case AbstractIntroElement.HTML:
             IntroHTML html = (IntroHTML) element;
             if (html.isInlined()) {
                 IntroText htmlText = html.getIntroText();
                 if (htmlText != null)
                     c = createText(parent, htmlText);
                 else {
                     IntroImage htmlImage = html.getIntroImage();
                     if (htmlImage != null)
                         c = createImage(parent, htmlImage);
                 }
             } else {
                 // embedded HTML, so we can show it from a link.
 String embddedLink = html.getSrc();
                 if (embddedLink == null)
                     break;
                 String linkText = StringUtil
                     .concat(
                         "<p><a HREF=\"http://org.eclipse.ui.intro/openBrowser?url=", //$NON-NLS-1$
 embddedLink, "\">", //$NON-NLS-1$
 Messages.HTML_embeddedLink, "</a></p>").toString(); //$NON-NLS-1$
 linkText = generateFormText(linkText);
                 c = createFormText(parent, linkText, null);
             }
             if (c != null)
                 updateLayoutData(c, element);
             break;
         case AbstractIntroElement.CONTENT_PROVIDER:
             IntroContentProvider provider = (IntroContentProvider) element;
             c = createContentProvider(parent, provider);
             updateLayoutData(c, element);
             break;
         case AbstractIntroElement.HR:
             IntroSeparator sep = (IntroSeparator)element;
             c = createSeparator(parent, sep);
             updateLayoutData(c, element);
             break;
             
         default:
             break;
         }
     }


     private void updateLayoutData(Control c, AbstractIntroElement element) {
         TableWrapData currentTd = (TableWrapData) c.getLayoutData();
         if (currentTd == null) {
             currentTd = new TableWrapData(TableWrapData.FILL,
                 TableWrapData.FILL);
             currentTd.grabHorizontal = true;
             c.setLayoutData(currentTd);
         }

         currentTd.colspan = styleManager
             .getColSpan((AbstractBaseIntroElement) element);
         currentTd.rowspan = styleManager
             .getRowSpan((AbstractBaseIntroElement) element);

     }

     private Composite createGroup(Composite parent, IntroGroup group) {
         String label = group.getLabel();
         String description = styleManager.getDescription(group);
         boolean expandable = group.isExpandable();
         boolean expanded = group.isExpanded();
         Composite client = null;
         Composite control = null;
         if (description != null || label != null || expandable) {
             int style = description != null ? Section.DESCRIPTION : SWT.NULL;
             if (expandable)
                 style |= Section.TWISTIE | Section.FOCUS_TITLE | Section.CLIENT_INDENT;
             if (expanded)
                 style |= Section.EXPANDED;
             Section section = toolkit.createSection(parent, style);
             if (label != null)
                 section.setText(label);
             if (description != null)
                 section.setDescription(description);
             colorControl(section, group);
             client = toolkit.createComposite(section, SWT.WRAP);
             section.setClient(client);
             control = section;
         } else {
             client = toolkit.createComposite(parent, SWT.WRAP);
             control = client;
         }

         TableWrapLayout layout = new TableWrapLayout();
         int numColumns = styleManager.getNumberOfColumns(group);
         numColumns = numColumns < 1 ? 1 : numColumns;
         layout.numColumns = numColumns;
         layout.makeColumnsEqualWidth = styleManager.getEqualWidth(group);
         layout.verticalSpacing = styleManager.getVerticalSpacing(group);
         layout.horizontalSpacing = styleManager.getHorizantalSpacing(group);
         client.setLayout(layout);
         // Util.highlight(client, SWT.COLOR_YELLOW);
 return control;
     }

     /**
      * Creates an Image Hyperlink from an IntroLink. Model object is NOT cached.
      *
      * @param body
      * @param link
      */
     private Control createImageHyperlink(Composite parent, IntroLink link) {
         Control control;
         Hyperlink linkControl;
         boolean showLinkDescription = styleManager.getShowLinkDescription();
         Image linkImage = styleManager.getImage(link, "link-icon", //$NON-NLS-1$
 ImageUtil.DEFAULT_LINK);

         if (showLinkDescription && link.getText() != null) {
             Composite container = toolkit.createComposite(parent);
             TableWrapLayout layout = new TableWrapLayout();
             layout.leftMargin = layout.rightMargin = 0;
             layout.topMargin = layout.bottomMargin = 0;
             layout.verticalSpacing = 0;
             layout.numColumns = 2;
             container.setLayout(layout);

             //Label ilabel = toolkit.createLabel(container, null);
 ImageHyperlink ilabel = toolkit.createImageHyperlink(container, SWT.NULL);
             ilabel.setImage(linkImage);
             ilabel.setHoverImage(styleManager.getImage(link, "hover-icon", //$NON-NLS-1$
 null));
             ilabel.setHref(link.getUrl());
             ilabel.addHyperlinkListener(hyperlinkAdapter);
             TableWrapData td = new TableWrapData();
             td.valign = TableWrapData.TOP;
             td.rowspan = 2;
             ilabel.setLayoutData(td);

             linkControl = toolkit.createHyperlink(container, null, SWT.WRAP);
             td = new TableWrapData(TableWrapData.LEFT, TableWrapData.BOTTOM);
             td.grabVertical = true;
             linkControl.setLayoutData(td);
             // Util.highlight(linkControl, SWT.COLOR_RED);
 // Util.highlight(container, SWT.COLOR_DARK_YELLOW);

             Control desc = createText(container, link.getIntroText());
             td = new TableWrapData(TableWrapData.FILL, TableWrapData.TOP);
             td.grabHorizontal = true;
             td.grabVertical = true;
             desc.setLayoutData(td);
             control = container;
         } else {
             ImageHyperlink imageLink = toolkit.createImageHyperlink(parent,
                 SWT.WRAP | SWT.CENTER);
             imageLink.setImage(linkImage);
             imageLink.setHoverImage(styleManager.getImage(link, "hover-icon", //$NON-NLS-1$
 null));
             TableWrapData td = new TableWrapData();
             td.grabHorizontal = true;
             imageLink.setLayoutData(td);
             linkControl = imageLink;
             control = linkControl;
         }
         linkControl.setText(link.getLabel());
         linkControl.setFont(PageStyleManager.getBannerFont());
         colorControl(linkControl, link);
         linkControl.setHref(link.getUrl());
         linkControl.addHyperlinkListener(hyperlinkAdapter);
         // Util.highlight(linkControl, SWT.COLOR_DARK_YELLOW);
 return control;
     }

     /**
      * Creates a forms Text or FormattedText.
      *
      * @param body
      * @param link
      */
     protected Control createText(Composite parent, IntroText text) {
         Color fg = styleManager.getColor(toolkit, text);
         boolean isBold = styleManager.isBold(text);
         // formatted case. If text is alredy formatted, the bold property is
 // ignored.
 if (text.isFormatted())
             return createFormText(parent, generateFormText(text.getText()), fg);

         // non formatted case.
 if (isBold)
             return createFormText(parent, generateBoldFormText(text.getText()),
                 fg);
         return createText(parent, text.getText(), fg);
     }

     private Control createFormText(Composite parent, String text, Color fg) {
         FormText formText = toolkit.createFormText(parent, false);
         formText.addHyperlinkListener(hyperlinkAdapter);
         try {
             formText.setText(text, true, true);
         } catch (SWTError e) {
             Log.error(e.getMessage(), e);
             return createText(parent, text, fg);
         }
         if (fg != null)
             formText.setForeground(fg);
         return formText;
     }


     private Control createText(Composite parent, String text, Color fg) {
         Label label = toolkit.createLabel(parent, text, SWT.WRAP);
         if (fg != null)
             label.setForeground(fg);
         return label;
     }



     protected Control createImage(Composite parent, IntroImage image) {
         Label ilabel = null;
         Image imageFile = styleManager.getImage(image);
         if (imageFile != null) {
             ilabel = toolkit.createLabel(parent, null, SWT.LEFT);
             ilabel.setImage(imageFile);
             if (image.getAlt() != null)
                 ilabel.setToolTipText(image.getAlt());
         }
         // for images, do not use default layout. Grab horizontal is not what we
 // want.
 if (ilabel!=null) {
             TableWrapData td = new TableWrapData();
             ilabel.setLayoutData(td);
         }
         return ilabel;
     }
     
     public Control createContentProvider(Composite parent,
             IntroContentProvider provider) {
         // If we've already loaded the content provider for this element,
 // retrieve it, otherwise load the class.
 // Create parent composite to hold dynamic content, and set layout
 // accordingly.
 Composite container = toolkit.createComposite(parent);
         TableWrapLayout layout = new TableWrapLayout();
         layout.topMargin = 0;
         layout.bottomMargin = 0;
         layout.leftMargin = 0;
         layout.rightMargin = 0;
         container.setLayout(layout);
         container.setData(provider);


         IIntroContentProvider providerClass = ContentProviderManager.getInst()
             .getContentProvider(provider);
         if (providerClass == null)
             // content provider never created before, create it.
 providerClass = ContentProviderManager.getInst()
                 .createContentProvider(provider, site);

         if (providerClass != null) {
             try {
                 providerClass.createContent(provider.getId(), container,
                     toolkit);
             } catch (Exception e) {
                 Log.error(
                     "Failed to create the content of Intro model content provider: " //$NON-NLS-1$
 + provider.getClassName(), e);
                 // null provider.
 providerClass = null;
             }
         }

         if (providerClass == null) {
             // we failed to create a provider class, create the embedded text.
 IntroText text = provider.getIntroText();
             if (text != null)
                 createText(container, text);
         }
         return container;
     }
     
     protected Control createSeparator(Composite parent, IntroSeparator sep) {
         String key = sep.getParentPage().getId()+".separator.fg"; //$NON-NLS-1$
 Color fg = styleManager.getColor(toolkit, key);
         //Composite l = toolkit.createCompositeSeparator(parent);
 Composite l = new Composite(parent, SWT.NULL);
         if (fg!=null)
             l.setBackground(fg);
         else
             l.setBackground(toolkit.getColors().getColor(IFormColors.SEPARATOR));
         TableWrapData td = new TableWrapData(TableWrapData.FILL,
                 TableWrapData.FILL);
         td.grabHorizontal = true;
         td.maxHeight = 1;
         l.setLayoutData(td);
         return l;
     }

     private void colorControl(Control elementControl,
             AbstractBaseIntroElement element) {
         Color fg = styleManager.getColor(toolkit, element);
         if (fg != null)
             elementControl.setForeground(fg);
         Color bg = styleManager.getBackgrond(toolkit, element);
         if (bg != null)
             elementControl.setBackground(bg);
     }


     /*
      * creates form text on a formatted string. A formatted string is any string
      * that has a " <" in it. If it starts with a <p> then it is assumed that
      * the text if a proper UI forms formatted text. If not, the <p> tag is
      * added.
      */
     private String generateFormText(String text) {
         StringBuffer sbuf = new StringBuffer ();
         sbuf.append("<form>"); //$NON-NLS-1$
 if (text.startsWith("<p>")) //$NON-NLS-1$
 sbuf.append(text);
         else {
             sbuf.append("<p>"); //$NON-NLS-1$
 sbuf.append(text);
             sbuf.append("</p>"); //$NON-NLS-1$
 }
         sbuf.append("</form>"); //$NON-NLS-1$
 return sbuf.toString();
     }

     /**
      * Will be only called for non formatted text.
      *
      * @param text
      * @return
      */
     private String generateBoldFormText(String text) {
         StringBuffer sbuf = new StringBuffer ();
         sbuf.append("<form>"); //$NON-NLS-1$
 sbuf.append("<p>"); //$NON-NLS-1$
 sbuf.append("<b>"); //$NON-NLS-1$
 sbuf.append(text);
         sbuf.append("</b>"); //$NON-NLS-1$
 sbuf.append("</p>"); //$NON-NLS-1$
 sbuf.append("</form>"); //$NON-NLS-1$
 return sbuf.toString();
     }

     /**
      * Check the filter state of the element. Only base elements have the filter
      * attribute.
      *
      * @param element
      * @return
      */
     private boolean getFilterState(AbstractIntroElement element) {
         if (element.isOfType(AbstractIntroElement.BASE_ELEMENT))
             return ((AbstractBaseIntroElement) element).isFiltered();
         return false;
     }


 }

